/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 */

import path from 'path';

import { Input, OutputFile, TaskDetails } from '@ohos/hvigor';
import * as fs from 'fs-extra';

import { BuildArtifactExtension } from '../../const/build-directory-const.js';
import {CommonConst, DefaultTargetConst} from '../../const/common-const.js';
import { DeviceTypeConst } from '../../const/device-type-const.js';
import { ModuleType } from '../../enum/module-type-enum.js';
import { CoreModuleModelImpl } from '../../model/module/core-module-model-impl.js';
import { SourceSetModel } from '../../model/source-set/source-set-model.js';
import { ModuleBuildProfile } from '../../options/build/module-build-profile.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { getSignRemoteHspMetadata } from '../../utils/remote-hsp-utils.js';
import { ModuleTargetData } from '../data/hap-task-target-data.js';
import { ModuleTaskService } from '../service/module-task-service.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { isSigned } from '../sign/sign-util.js';
import { OhosHapTask } from '../task/ohos-hap-task.js';

import DistroFilterBuildOpt = ModuleBuildProfile.DistroFilterBuildOpt;

/**
 * hap包的生成信息，该配置只对当前Target包生效，每个Target生成一个文件
 */
interface TargetMetadata {
  hapName?: string;
  hspName?: string;
  dependRemoteHsps?: RemoteHspMetadata[];
  deviceTypes: string[] | undefined;
  isSigned: boolean;
  entryModule: EntryModuleOpt | undefined;
}

/**
 * 该标签描述当前Feature所关联的Entry的相关信息，标签值为json对象。该标签可缺省，只在Feature类型时才出现
 */
interface EntryModuleOpt {
  moduleName: string;
  distroFilter: DistroFilterBuildOpt | undefined;
  deviceType: string[] | undefined;
}

export interface RemoteHspMetadata {
  hspName: string;
  hspVersion: string;
  hspPath: string;
  soPath: string;
}

/**
 * 构建根据feature与entry关联关系生成对应output_metadata.json
 * path:{buildRoot}/{moduleName}/build/{productName}/intermediates/hap_metadata/{targetName}/output_metadata.json
 */
export abstract class AbstractGenerateMetadata extends OhosHapTask {
  protected readonly sourceSetModel: SourceSetModel;
  readonly _log = OhosLogger.getLogger(AbstractGenerateMetadata.name);
  @Input() private readonly remoteHspMetaData: RemoteHspMetadata[] | undefined;

  protected constructor(targetService: TargetTaskService, taskDetails: TaskDetails) {
    super(targetService, taskDetails);
    this.sourceSetModel = targetService.getTargetData().getModuleSourceSetModel();

    // 生成remoteHsp的metadata文件
    this.remoteHspMetaData = getSignRemoteHspMetadata(this.service, this.targetService.getTargetData().getProduct().name);
  }

  @Input() private get artifactName(): string | undefined {
    return this.targetData.getTargetOpt().output?.artifactName;
  }

  @Input() private get relatedEntryModules(): string[] | undefined {
    return this.targetData.getRelatedEntryModules();
  }

  @Input() private get targetDeviceType(): string[] {
    return this.targetData.getTargetDeviceType();
  }

  @Input() private get isSigned(): boolean {
    return isSigned(this.projectModel);
  }

  @OutputFile() private get outputFilePath(): string {
    return this.moduleModel.isHspModule() ? this.pathInfo.getIntermediatesHspOutputMetadata() : this.pathInfo.getIntermediatesOutputMetadata();
  }

  protected doTaskAction() {

    // 此处会影响generateTargetMetadata生成，对于har来说存在ohosTest会生成metadata
    if (this.moduleModel.isHarModule() && !(this.targetName === DefaultTargetConst.OHOS_TEST_TARGET)) {
      return;
    }
    const targetMetadata = this.generateTargetMetadata(this.targetData);
    fs.outputJSONSync(this.outputFilePath, targetMetadata);
  }

  initTaskDepends() {
    // no task
  }

  /**
   * 根据target生成output_metadata.json
   *
   * @param targetData featureTargetData
   * @private
   */
  private generateTargetMetadata(targetData: ModuleTargetData): TargetMetadata[] {
    const isRequireSigned = this.isSigned;
    const targetMetadataList: TargetMetadata[] = [];
    const moduleType = targetData.getModuleModel().getModuleType();
    switch (moduleType) {
      case ModuleType.Entry:
        targetMetadataList.push(this.generateModuleMetadata(targetData, isRequireSigned));
        break;
      case ModuleType.Feature:
        if (ModuleTaskService.checkEntryModules(moduleType, this.relatedEntryModules)) {
          for (const entryName of this.relatedEntryModules!) {
            targetMetadataList.push(this.generateFeatureMetadata(targetData, entryName, isRequireSigned));
          }
        } else {
          targetMetadataList.push(this.generateModuleMetadata(targetData, isRequireSigned));
        }
        break;
      case ModuleType.Shared:
        targetMetadataList.push(this.generateHspMetadata(targetData, isRequireSigned));
        break;
      case ModuleType.Har:
        targetMetadataList.push(this.generateHarMetadata(targetData, isRequireSigned));
        break;
      default:
        break;
    }
    return targetMetadataList;
  }

  /**
   * 生成entry模块 或 无关联entry模块信息的feature模块的metadata对象
   * @param targetData ModuleTargetData
   * @param isSigned 是否签名
   * @private
   */
  private generateModuleMetadata(targetData: ModuleTargetData, isSigned: boolean): TargetMetadata {
    const moduleName = this.service.getModuleModel().getName();
    const relatedEntryTargetData = targetData.findRelatedTargetData(moduleName);
    const relatedTargetName = relatedEntryTargetData?.getTargetName();
    let outputHapName = '';
    let entryDeviceTypes: string[] | undefined = [];
    targetData.getTargetDeviceTypeClasses().forEach((deviceTypeClass) => {
      // 混合设备场景下，瘦设备hap包名需要带上lite后缀
      const liteSuffix = deviceTypeClass === DeviceTypeConst.LITE && !targetData.isSingleDeviceTypeTarget() ? `-${deviceTypeClass}` : '';
      const signSuffix = isSigned ? 'signed' : 'unsigned';
      const artifactName = this.artifactName;
      outputHapName = artifactName
        ? `${artifactName}${isSigned ? '' : '-unsigned'}${BuildArtifactExtension.DOT_HAP}`
        : `${moduleName}-${relatedTargetName}${liteSuffix}-${signSuffix}${BuildArtifactExtension.DOT_HAP}`;
      entryDeviceTypes = relatedEntryTargetData?.getTargetDeviceType().filter((deviceType) => {
        return deviceTypeClass === DeviceTypeConst.LITE ? DeviceTypeConst.LITE_DEVICES.includes(deviceType) : DeviceTypeConst.RICH_DEVICES.includes(deviceType);
      });
    });

    return {
      hapName: outputHapName,
      deviceTypes: entryDeviceTypes,
      isSigned,
      entryModule: undefined,
      dependRemoteHsps: this.remoteHspMetaData,
    };
  }

  /**
   * 生成存在关联entry信息的feature模块的metadata对象
   * @param targetData ModuleTargetData
   * @param entryName 关联entry的moduleName
   * @param isSigned 是否签名
   * @private
   */
  private generateFeatureMetadata(targetData: ModuleTargetData, entryName: string, isSigned: boolean): TargetMetadata {
    const targetName = targetData.getTargetName();
    const moduleName = targetData.getModuleModel().getName();
    const artifactName = this.artifactName;
    const outputHapName = artifactName
      ? `${artifactName}${isSigned ? '' : '-unsigned'}${BuildArtifactExtension.DOT_HAP}`
      : `${moduleName}-${entryName}-${targetName}-${isSigned ? 'signed' : 'unsigned'}${BuildArtifactExtension.DOT_HAP}`;
    const featureDeviceTypes = this.targetDeviceType;
    const relatedEntryTargetData = this.targetData.findRelatedTargetData(entryName);

    // entryModuleOpt
    const entryModuleOpt: EntryModuleOpt = {
      moduleName: entryName,
      distroFilter: this.getDistroFilterObj(relatedEntryTargetData!),
      deviceType: relatedEntryTargetData?.getTargetDeviceType() ?? [],
    };

    return {
      hapName: outputHapName,
      deviceTypes: featureDeviceTypes,
      isSigned,
      entryModule: entryModuleOpt,
      dependRemoteHsps: this.remoteHspMetaData,
    };
  }

  /**
   * 生成hsp的metadata对象
   * @param targetData ModuleTargetData
   * @param isSigned 是否签名
   * @private
   */
  private generateHspMetadata(targetData: ModuleTargetData, isSigned: boolean): TargetMetadata {
    const targetName = targetData.getTargetName();
    const moduleName = targetData.getModuleModel().getName();
    const artifactName = this.artifactName;
    const outputHspName = artifactName
      ? `${artifactName}${isSigned ? '' : '-unsigned'}${BuildArtifactExtension.DOT_HSP}`
      : `${moduleName}-${targetName}-${isSigned ? 'signed' : 'unsigned'}${BuildArtifactExtension.DOT_HSP}`;
    return {
      hspName: outputHspName,
      deviceTypes: this.targetDeviceType,
      isSigned,
      entryModule: undefined,
      dependRemoteHsps: this.remoteHspMetaData,
    };
  }

  /**
   * 生成hsp的metadata对象,只在ohosTest场景下生成
   * @param targetData ModuleTargetData
   * @param isSigned 是否签名
   * @private
   */
  private generateHarMetadata(targetData: ModuleTargetData, isSigned: boolean): TargetMetadata {
    const targetName = targetData.getTargetName();
    const moduleName = targetData.getModuleModel().getName();
    const artifactName = this.artifactName;
    const outputHapName = artifactName
      ? `${artifactName}${isSigned ? '' : '-unsigned'}${BuildArtifactExtension.DOT_HAP}`
      : `${moduleName}-${targetName}-${isSigned ? 'signed' : 'unsigned'}${BuildArtifactExtension.DOT_HAP}`;
    return {
      hapName: outputHapName,
      deviceTypes: this.targetDeviceType,
      isSigned,
      entryModule: undefined,
      dependRemoteHsps: this.remoteHspMetaData,
    };
  }

  protected abstract getDistroFilterObj(relatedEntryTargetData: ModuleTargetData): DistroFilterBuildOpt | undefined;

  protected getRelatedEntryJsonList(jsonFile: string): string[] {
    const relatedEntryJsonList: string[] = [];
    const moduleType = this.moduleModel.getModuleType();
    const relatedEntryModules = this.targetData.getRelatedEntryModules();
    if (!ModuleTaskService.checkEntryModules(moduleType, relatedEntryModules)) {
      return [];
    }
    const projectDir = this.moduleModel.getParentProject().getProjectDir();
    for (const relatedEntryModule of relatedEntryModules!) {
      const moduleModel = this.projectModel.getModuleModelByName(relatedEntryModule) as CoreModuleModelImpl;
      const moduleNodeDir = moduleModel.getModule().getNodeDir();
      const entryJsonPath = path.resolve(projectDir, moduleNodeDir, `src/main/${jsonFile}`);
      if (fs.existsSync(entryJsonPath)) {
        relatedEntryJsonList.push(entryJsonPath);
      }

      const entryBuildJsonPath = path.resolve(projectDir, moduleNodeDir, CommonConst.PROFILE_JSON5);
      if (fs.existsSync(entryBuildJsonPath)) {
        relatedEntryJsonList.push(entryBuildJsonPath);
      }
    }
    return relatedEntryJsonList;
  }
}
